package com.douban.media
{
	import com.adobe.serialization.json.JSON;
	import com.douban.event.ListEvent;
	import com.douban.event.PlayCompleteEvent;
	import com.douban.event.PlayErrorEvent;
	import com.douban.event.PlayProgressEvent;
	import com.douban.event.PlayRepeatEvent;
	import com.douban.event.SongEvent;
	import com.douban.utils.Format;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.media.Sound;
	import flash.net.SharedObject;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.setTimeout;
	
	import mx.collections.ArrayCollection;
	
	import net.sevear.chaos.ChaosTrackManager;
	import net.sevear.chaos.download.DBRadioKeeper;
	import net.sevear.chaos.download.Predef;
	import net.sevear.chaos.event.ChaosTrackEvent;
	import net.sevear.chaos.event.RecommendEvent;
	import net.sevear.chaos.recommend.RecommendManager;
	import net.sevear.chaos.utils.Logger;
	import net.sevear.chaos.view.ChaosLogPanel;
	import net.sevear.chaos.vo.RecommendInfo;
	import net.sevear.chaos.vo.TrackInfo;
	
	public class DBRadio extends EventDispatcher
	{
		public static const PLAYOUT:String = "p";
		public static const PLAYED:String = "e";
		public static const LIKE:String = "r";
		public static const BAN:String = "b";
		public static const UNLIKE:String = "u";
		public static const NEWs:String = "n";
		public static const SKIP:String = "s";
		
		public static const START:String = "start";
		public static const PLAY_REC_SONG:String = "R";
		public static const LIST_ZERO_ERROR:String = "lze";
		public static const LIST_PARSE_ERROR:String = "lpe";
		public static const LIST_SERVER_ERROR:String = "lse";
		public static const LIST_IOERROR:String = "lioe";
		
		public static const SWITCH_TRACK_SID:int = -1;
		
		[Embed(source='/res/sound/switch2.mp3')]
		private var SWITCH_SOUND:Class;
		
		private var so:SharedObject;
		private var playSong:TrackInfo;
		private var player:AudioPlayer;
		private var playList:ArrayCollection;
		private var dbList:ArrayCollection;
		private var startNewList:Boolean ;
		private var _channel:String = "";
		private var timeToWait:int;
		private var pauseTime:int;
		private var repLoader:URLLoader;
		private var reportedId:String;
		private var loader:URLLoader;
		private var HOST:String;
		private var requrl:String;
		private var LISTURL:String;
		private var context:String;
		private var switchSong:TrackInfo;
		private var recommend:RecommendManager = RecommendManager.getInstance();
		private var logger:Logger = Logger.getInstance();
		private var switchSound:Sound;
		
		private var curRepeat:int;
		private var totalRepeat:int;
		public static const MAX_REPEAT:int = 640;
		
		public function DBRadio()
		{
			HOST = Predef.DOUBAN_APP_HOST;
			LISTURL = Format.radioListURL();
			playList = new ArrayCollection();
			startNewList = true;
			//默认私人电台
			_channel = "0";
			timeToWait = 0;
			pauseTime = 0;
			player = new AudioPlayer();
			player.addEventListener(PlayCompleteEvent.COMPLETE, onComplete);
			player.addEventListener(PlayErrorEvent.PLAY_ERROR, onPlayError);
			player.addEventListener(PlayErrorEvent.ERROR_LOAD_TIMEOUT,onPlayError);
			player.addEventListener(PlayErrorEvent.ERROR_BUFFERING,onPlayError);
			player.addEventListener(PlayProgressEvent.PLAY_ONPROGRESS, onProgress);
			
			recommend.addEventListener(RecommendEvent.GET_NEW_RECOMMENDLIST,onGetRecommend);
			ChaosTrackManager.getInstance().addEventListener(ChaosTrackEvent.NEW_CHAOS_REPLAY_ARRIVED,onNewReplayArrived);
			so = SharedObject.getLocal("douchaos", "/");
			
			if (so.data.history == undefined)
			{
				so.clear();
				so.data.history = "";
				so.flush();
			}
			
			loader = new URLLoader();
			loader.addEventListener(IOErrorEvent.IO_ERROR, report_ioError);
			loader.addEventListener(Event.COMPLETE,loadNewPlayListComplete);
			repLoader = new URLLoader();
			repLoader.addEventListener(IOErrorEvent.IO_ERROR, report_ioError);
			initSwitchSound();
		}
		
		private static var _instance:DBRadio;
		public static function getInstance():DBRadio{
			if(_instance == null){
				_instance = new DBRadio();
			}
			return _instance;
		}
		
		public function start():void{
			if (player.status == AudioPlayer.PLAY)
			{
				return;
			}
			startNewList = true;
			requireList(0, NEWs);
		}
		
		public function stop():void{
			if(player.status != AudioPlayer.STOP){
				player.stop();
			}
			try{
				loader.close();
			}catch(e:Error){
				
			}
		}
		
		public function pause():void{
			pauseTime = getTimer();
			player.pause();
		}
		
		public function resume():void{
			player.play();
			if ((getTimer() - pauseTime) / 1000 > 60 * 30)
			{
				clearDBPlaylist();
				requireList(0, NEWs);
			}
		}
		
		private function getTimer():Number{
			return (new Date()).time;
		}
		
		public function skip():void{
			onStatus(SKIP);
			if(playSong.subtype == TrackInfo.RECOMMEND){
				var recommends:Array = recommend.getRecomendItems(playSong.sid);
				for each(var reItem:RecommendInfo in recommends){
					recommend.setRecommendPassed(reItem.id);
				}
			}
			playNextTrack();
		}
		
		public function like():void{
			playSong.like = true;
			startNewList = false;
			handleRecommendLike(playSong);
			onStatus(LIKE);
		}
		
		public function unlike():void{
			playSong.like = false;
			startNewList = false;
			onStatus(UNLIKE);
		}
		
		public function get isLiked():Boolean{
			if (playSong!=null)
			{
				return playSong.like;
			}
			
			return false;
		}
		
		public function ban():void{
			onStatus(BAN);
			if(playSong.subtype == TrackInfo.RECOMMEND){
				var recommends:Array = recommend.getRecomendItems(playSong.sid);
				for each(var reItem:RecommendInfo in recommends){
					recommend.setRecommendBaned(reItem.id);
				}
			}
			playNextTrack();
		}
		
		public function repeat(count:int = MAX_REPEAT):void{
			totalRepeat = Math.min(MAX_REPEAT,Math.max(0,count));
			if(totalRepeat>0){
				dispatchEvent(new PlayRepeatEvent(PlayRepeatEvent.SET_TRACK_REPEAT,curRepeat,totalRepeat));
			}
		}
		
		public function unrepeat():void{
			if(totalRepeat>0){
				dispatchEvent(new PlayRepeatEvent(PlayRepeatEvent.CANCEL_TRACK_REPEAT,curRepeat,totalRepeat));
				totalRepeat = curRepeat = 0;
			}
		}
		
		public function set volumn(value:Number):void{
			player.volume = value;
		}
		
		public function get volumn():Number{
			return player.volume;
		}
		
		public function set channel(value:String):void{
			if (value !== _channel)
			{
				_channel = value;
				if (player.status === AudioPlayer.PLAY)
				{
					clearDBPlaylist();
					requireList(0, NEWs);
					playNextTrack();
				}
			}
		}
		
		public function get channel():String{
			return _channel;
		}
		
		public function get curSong():TrackInfo{
			return playSong
		}
		
		private function initSwitchSound():void{
			switchSound = new SWITCH_SOUND();
		}
		
		private function handleRecommendLike(track:TrackInfo):void{
			if(track.subtype == TrackInfo.RECOMMEND){
				var recommends:Array = recommend.getRecomendItems(track.sid);
				for each(var reItem:RecommendInfo in recommends){
					recommend.setRecommendLiked(reItem.id);
				}
			}
		}
		
		private function loadNewPlayListComplete(e:Event):void{
			var rst:*;
			try{
				rst = JSON.decode((e.target as URLLoader).data as String);
			}catch(e:Error){
				loader_ioError("json_parse_error");
				logger.logError("json parse error:" + e.toString());
			}
			
			var jsonSongs:Array = rst.song as Array;
			if(jsonSongs==null){
				clearDBPlaylist();
			}else if(jsonSongs.length == 0){
				//TODO:检查登录、尝试再次获取
			}else{
				var item:TrackInfo;
				var song:Object;
				for (var songidx:int = 0;songidx<jsonSongs.length;songidx++){
					item = new TrackInfo();
					playList.addItem(item);
					song = jsonSongs[songidx];
					parseRemoteTrackInfo(item,song);
				}
				if(playList.length>0){
					timeToWait = 0;
					if (startNewList)
					{
						startNewList = false;
						playNextTrack();
					}
				}else{
					loader_ioError("zero_song");
				}
			}
		}
		
		
		private function getFirstNonAdTypeSong(rst:Array):Object{
			for each(var song:Object in rst){
				if(song.subtype!=TrackInfo.ADTYPE){
					return song;
				}
			}
			
			return null;
		}
		
		private function parseRemoteTrackInfo(item:TrackInfo,song:Object):void{
			if(item!=null && song!=null){
				item.name = song.title as String;
				item.albumName = song.albumtitle as String;
				item.albumDBURL = song.album as String;
				item.artistName = song.artist;
				item.aid = parseInt(song.aid as String);
				item.albumImgURL = (song.picture as String).replace(/mpic/,"lpic");
				item.company = song.company as String;
				item.listenURL = song.url as String;
				item.downURLs[DBRadioKeeper.SOURCE_DB_RADIO] = [item.listenURL];
				item.length = song.length as int;
				item.like = song.like=="1";
				item.rating_avg = song.rating_avg as Number;
				item.sid = parseInt(song.sid as String);
				item.ssid = song.ssid as String;
				item.subtype = song.subtype as String;
				item.public_time = song.public_time as String;
			}
		}
		
		private function playNormalSong():void{
			var e:SongEvent;
			if (playList.length === 0)
			{
				requireList(playSong.sid, PLAYOUT);
			}
			e = new SongEvent();
			e.data = {type:START, song:playSong, channel:_channel};
			dispatchEvent(e);
			var playType:int;
			if(playSong.subtype!=TrackInfo.LOCAL){
				playType = AudioPlayer.TYPE_DOUBAN;
			}else{
				playType = AudioPlayer.TYPE_LOCAL;
			}
			player.play(playSong.listenURL,playType,playSong.length);
		}
		
		private function checkPlayListLenValidate():Boolean{
			return playList.length >= 1;
		}
		
		private function checkRecommendSwitch(recommendStay:Boolean):Boolean{
			return	recommendStay == false && playSong!=null && playSong.subtype!=TrackInfo.RECOMMEND 
				&& playList.length>0 &&
				(playList.getItemAt(0) as TrackInfo).subtype == TrackInfo.RECOMMEND
		}
		
		private function playNext(recommendStay:Boolean = false):void
		{
			player.stop();
			if (checkPlayListLenValidate()){
				if(totalRepeat>0 && curRepeat<totalRepeat){
					playNormalSong();
				}else{
					if(totalRepeat>0 && curRepeat>=totalRepeat){
						unrepeat();
					}
					if(checkRecommendSwitch(recommendStay)){
						switchSound.play(0);
						setTimeout(playNext,1500,true);
					}else{
						playSong = playList.source.shift() as TrackInfo;
						
						if(playSong.like){
							handleRecommendLike(playSong);
						}
						
						if(playSong.subtype == TrackInfo.ADTYPE){
							logger.logInfo("Skip a adtype song");
							playNextTrack();
						}else{
							playNormalSong();
						}
					}
				}
			}else{
				startNewList = true;
			}
		}
		
		private function requireList(sid:int,type:String):void{
			var h:String = String(so.data.history);
			requrl = LISTURL + "&type=" + type + 
				(type != NEWs ? ("&sid=" + (sid==0?"null":sid.toString())) : ("")) + "&h=" + h + "&channel=" + _channel;
			if(type==NEWs) 
				requrl +='&du=0';
			else if(type == SKIP) 
				requrl +='&du=2';
			
//			logger.logInfo("now loading:", requrl);
			try
			{
				loader.close();
			}
			catch (e:Error)
			{
			}
			loader.load(new URLRequest(requrl));
		}
		
		private function waitTime():int
		{
			timeToWait = timeToWait * 2 + 1;
			return timeToWait;
		}
		
		private function onStatus(newStatus:String):void{
			var sid:String;
			var history:Array;
			var f:Function;
			var repurl:String;
			var type:String = newStatus;
			var songEv:SongEvent = new SongEvent();
			songEv.data = {type:type, song:playSong, channel:_channel};
			dispatchEvent(songEv);
			if (playSong.subtype != TrackInfo.ADTYPE)
			{
				sid = playSong.sid.toString();
				history = String(so.data.history).split("|");
				f = function (item:*, index:int, array:Array):Boolean
				{
					return parseInt(item) != parseInt(sid);
				};
				history = history.filter(f);
				history.push(sid + ":" + (type == PLAYED ? (PLAYOUT) : (type)));
				so.data.history = history.slice(-20).join("|");
				so.flush();
			}
			
			var nextSong:TrackInfo = playList.length>0?playList.getItemAt(0) as TrackInfo:null;
			if(nextSong!=null && nextSong.subtype==TrackInfo.RECOMMEND){
			}else{
				if (type == PLAYED)
				{
					repurl = LISTURL + "&type=e&sid=" + playSong.sid + "&channel=" + _channel;
					repurl = Format.signUrl(repurl);
					try
					{
						repLoader.load(new URLRequest(repurl));
					}
					catch (e:Error)
					{
						logger.logError(e.message);
					}
				}
				else
				{
					requireList(playSong.sid, type);
				}
				
				if ([LIKE, UNLIKE, BAN, SKIP].indexOf(type) != -1)
				{
					clearDBPlaylist();
				}
			}
		}
		
		private function onComplete(e:PlayCompleteEvent):void
		{
			onStatus(PLAYED);
			if(totalRepeat>0 && curRepeat<totalRepeat)
				curRepeat++;
			else{
				unrepeat();
			}
			playNext();
			return;
		}
		
		private function loader_ioError(msg:String) : void
		{
			var ev:ListEvent = new ListEvent();
			ev.message = {error:LIST_IOERROR, msg:""};
			dispatchEvent(ev);
			var wTime:int = waitTime();
			if (wTime == 5)
			{
				logger.logError("always_load_fail ", "ra005");
			}
//			logger.logInfo("retry in " + wTime + " secs");
			var f:Function = function ():void
			{
				try
				{
					loader.close();
				}
				catch (e:Error)
				{
				}
				if(msg == "zero_song")
					requireList(0, NEWs);
				else
					loader.load(new URLRequest(requrl));
			};
			setTimeout(f, wTime * 1000);
			return;
		}
		
		private function onPlayError(param1:PlayErrorEvent):void
		{
			switch(param1.error)
			{
				case PlayErrorEvent.ERROR_IOERROR:
				{
					if (playList.length <= 1)
					{
						requireList(playSong.sid, PLAYOUT);
					}
//					playNext();
					playNextTrack();
					break;
				}
				case PlayErrorEvent.ERROR_LOAD_TIMEOUT:
				{
					ChaosLogPanel.getInstance().addTempBasicInfo(curSong.name+"加载超时...");
					if (playList.length <= 1)
					{
						requireList(playSong.sid, PLAYOUT);
					}
					playNextTrack();
					break;
				}
				case PlayErrorEvent.ERROR_BUFFERING:
				{
					if (reportedId != playSong.sid.toString())
					{
						logger.logError("play_slow " + player.url, "ra008");
						ChaosLogPanel.getInstance().addTempBasicInfo("正在缓冲...");
						reportedId = playSong.sid.toString();
					}
					break;
				}
				default:
					break;
			}
		}
		
		private function report_ioError(param1:IOErrorEvent) : void
		{
			logger.logError("fail when reporting:" + param1.toString());
			loader_ioError(param1.text);
			return;
		}
		
		private function onProgress(e:PlayProgressEvent):void
		{
			dispatchEvent(new PlayProgressEvent(e.progress));
		}
		
		//FIXME:有可能playlist长度会特别大。
		private function onGetRecommend(e:RecommendEvent):void{
			var list:ArrayCollection = e.data as ArrayCollection;
			this.playList.addAllAt(list,0);
		}
		
		private function clearDBPlaylist():void{
			var newList:ArrayCollection = new ArrayCollection();
			for each(var track:TrackInfo in this.playList){
				if([TrackInfo.INSTANT,TrackInfo.LOCAL,TrackInfo.RECOMMEND].indexOf(track.subtype)>=0){
					newList.addItem(track);
				}
			}
			this.playList = newList;
		}
		
		private function onNewReplayArrived(e:ChaosTrackEvent):void{
			if (player.status === AudioPlayer.PLAY){
				this.playList.addItemAt(e.trackInfo,0);
				playNextTrack();
			}
		}
		
		private function playNextTrack():void{
			unrepeat();
			playNext();
		}
	}
}